import Taro from "@tarojs/taro"
interface eventList {
	"showcase.open": Function
	"showcase.close": Function
}

type ValidEventTypes = string | symbol | object
type EventNames<T extends ValidEventTypes> = T extends string | symbol ? T : keyof T
type EventListener<T extends ValidEventTypes, K extends EventNames<T>> = T extends string | symbol
	? (...args: any[]) => void
	: (...args: ArgumentMap<Exclude<T, string | symbol>>[Extract<K, keyof T>]) => void
type ArgumentMap<T extends object> = {
	[K in keyof T]: T[K] extends (...args: any[]) => void ? Parameters<T[K]> : T[K] extends any[] ? T[K] : any[]
}
type EventArgs<T extends ValidEventTypes, K extends EventNames<T>> = Parameters<EventListener<T, K>>

export default new (class<EventTypes extends ValidEventTypes = string | symbol> {
	constructor() {
		this.event = Taro.eventCenter
	}
	private eventMap: Map<EventNames<EventTypes>, Set<EventListener<EventTypes, EventNames<EventTypes>>>> = new Map()
	eventNames(): EventNames<EventTypes>[] {
		return Array.from(this.eventMap.keys())
	}
	listeners<T extends EventNames<EventTypes>>(name: T): EventListener<EventTypes, T>[] | undefined {
		return this.eventMap.get(name) ? Array.from(this.eventMap.get(name)!) : undefined
	}
	on<T extends EventNames<EventTypes>>(event: T, fn: EventListener<EventTypes, T>) {
		this.eventMap.has(event) || this.eventMap.set(event, new Set())
		this.eventMap.get(event)!.add(fn)
		this.event.on(event as string, fn)
		return this
	}
	off<T extends EventNames<EventTypes>>(event: T, fn?: EventListener<EventTypes, T>) {
		if (fn) {
			this.eventMap.get(event)?.delete(fn)
		} else {
			this.eventMap.delete(event)
		}
		this.event.off(event as string, fn)
		return this
	}
	once<T extends EventNames<EventTypes>>(event: T, fn: EventListener<EventTypes, T>) {
		this.event.once(event as string, fn)
		return this
	}
	emit<T extends EventNames<EventTypes>>(event: T, ...args: EventArgs<EventTypes, T>): boolean {
		if (!this.eventMap.has(event)) {
			console.warn(`<${event}> 事件未注册或为Once事件!`)
		}
		return this.event.trigger(event as string, ...args)
	}
	private event: Taro.Events
})<eventList>()
